// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <iostream>
#include <cstring>
#include <time.h>
#include <chrono>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <algorithm>

#include "QueueModule.h"
#include "Log.h"

namespace master {

int QueueModules::GetDealQueueSize()
{
    return dealQueue.get_lengh();
}

int QueueModules::PushReadFd(uint8_t *inData, uint32_t inLen, int fd, std::string requestId)
{
    std::shared_ptr<ReadDataT> pReadQueue = std::make_shared<ReadDataT>();
    pReadQueue->inFd = fd;
    pReadQueue->requestId = requestId;

    this->readQueue.push(pReadQueue);
    
    return 0;
}

int QueueModules::PushDealData(uint8_t *inData, uint32_t inLen, 
                                  uint16_t cmdCtl, int fd, std::string requestId)
{
    std::shared_ptr<DealDataT> pDealQueue = std::make_shared<DealDataT>();
    
    auto pData = std::shared_ptr<uint8_t>(new uint8_t[MAX_QUEUE_DATA_LEN], std::default_delete<uint8_t[]>());
    pDealQueue->pClientRData = pData;

    pDealQueue->fd = fd;
    pDealQueue->cmdCtl = cmdCtl;
    pDealQueue->requestId = requestId;
    pDealQueue->bufLen = inLen;

    int ret = memcpy_s(pDealQueue->pClientRData.get(), inLen, inData, inLen);
    if (ret != 0) {
        LOG_ERROR_FMT("Push deal data failed ret = %d", ret);
        return -1;
    }
    this->dealQueue.push(pDealQueue);
    
    return 0;
}

int QueueModules::PushWriteData(uint8_t *inData, uint32_t inLen, 
                                uint16_t cmdCtl, int fd, std::string requestId)
{
    std::shared_ptr<WriteDataT> pWriteQueue = std::make_shared<WriteDataT>();
    
    auto pData = std::shared_ptr<uint8_t>(new uint8_t[MAX_QUEUE_DATA_LEN], std::default_delete<uint8_t[]>());
    pWriteQueue->pClientWData = pData;

    pWriteQueue->fd = fd;
    pWriteQueue->cmdCtl = cmdCtl;
    pWriteQueue->requestId = requestId;
    pWriteQueue->bufLen = inLen;

    int ret = memcpy_s(pWriteQueue->pClientWData.get(), inLen, inData, inLen);
    if (ret != 0) {
        LOG_ERROR_FMT("Push write data failed ret = %d", ret);
        return -1;
    }
    this->writeQueue.push(pWriteQueue);

    return 0;
}

int QueueModules::Push(QueueType queueType, uint8_t *inData, 
                        uint32_t inLen, uint16_t cmdCtl, int fd, std::string requestId)
{
    if (nullptr == inData || inLen > MAX_QUEUE_DATA_LEN) {
        LOG_ERROR_FMT("Params is invalid inData = 0x%x inLen = %u", inData, inLen);
        return -1;
    }

    switch (queueType) {
        case QUEUE_READ :
            if (this->PushReadFd(inData, inLen, fd, requestId) != 0) {
                LOG_ERROR_FMT("Epoll in push read fd error");
                return -1;
            }
            break;
        case QUEUE_DEAL :
            if (this->PushDealData(inData, inLen, cmdCtl, fd, requestId) != 0) {
                LOG_ERROR_FMT("Epoll in push deal data error");
                return -1;
            }
            break;
        case QUEUE_WRITE :
            if (this->PushWriteData(inData, inLen, cmdCtl, fd, requestId) != 0) {
                LOG_ERROR_FMT("Epoll in push write data error");
                return -1;
            }
            break;
        default :
            LOG_ERROR_FMT("Epoll in push data queue type error");
            break;
    }

    return 0;
}

int QueueModules::PopReadFd(uint8_t *outData, uint32_t *outLen, int *fd, std::string &requestId)
{
    std::shared_ptr<ReadDataT> pReadQueue;
    
    this->readQueue.wait_and_pop(pReadQueue);
    *fd = pReadQueue->inFd;
    requestId = pReadQueue->requestId;

    return 0;
}

int QueueModules::PopDealData(uint8_t *outData, uint32_t *outLen, 
                                  uint16_t *cmdCtl, int *fd, std::string &requestId)
{
    std::shared_ptr<DealDataT> pDealQueue;
    this->dealQueue.wait_and_pop(pDealQueue);

    int ret = memcpy_s(outData, pDealQueue->bufLen, pDealQueue->pClientRData.get(), pDealQueue->bufLen);
    if (ret != 0) {
        LOG_ERROR_FMT("Push deal data failed ret = %d", ret);
        return -1;
    }
    *outLen = pDealQueue->bufLen;
    *cmdCtl = pDealQueue->cmdCtl;
    *fd = pDealQueue->fd;
    requestId = pDealQueue->requestId;
    
    return 0;
}

int QueueModules::PopWriteData(uint8_t *outData, uint32_t *outLen, 
                                   uint16_t *cmdCtl, int *fd, std::string &requestId)
{
    std::shared_ptr<WriteDataT> pWriteQueue;
    
    this->writeQueue.wait_and_pop(pWriteQueue);
    int ret = memcpy_s(outData, pWriteQueue->bufLen, pWriteQueue->pClientWData.get(), pWriteQueue->bufLen);
    if (ret != 0) {
        LOG_ERROR_FMT("Push write data failed ret = %d", ret);
        return -1;
    }
    *outLen = pWriteQueue->bufLen;
    *cmdCtl = pWriteQueue->cmdCtl;
    *fd = pWriteQueue->fd;
    requestId = pWriteQueue->requestId;
    return 0;
}

int QueueModules::Pop(QueueType queueType, uint8_t *outData, uint32_t *outLen, 
                       uint16_t *cmdCtl, int *fd, std::string &requestId)
{
    switch (queueType) {
        case QUEUE_READ :
            if (this->PopReadFd(outData, outLen, fd, requestId) != 0) {
                LOG_ERROR_FMT("Epoll in pop read fd error");
                return -1;
            }
            break;
        case QUEUE_DEAL :
            if (this->PopDealData(outData, outLen, cmdCtl, fd, requestId) != 0) {
                LOG_ERROR_FMT("Epoll in pop deal data error");
                return -1;
            }
            break;
        case QUEUE_WRITE :
            if (this->PopWriteData(outData, outLen, cmdCtl, fd, requestId) != 0) {
                LOG_ERROR_FMT("Epoll in push write data error");
                return -1;
            }
            break;
        default :
            LOG_ERROR_FMT("Epoll in push data queue type error");
            break;
    }
    return 0;
}
};